from textwrap import dedent
from crewai import Agent, Task, Crew
from datetime import datetime
from crewai_tools import (
    DirectoryReadTool,
    DOCXSearchTool,
    FileReadTool,
    SerperDevTool,
    WebsiteSearchTool
)
from langchain_community.llms import Ollama
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
import os
from docx import Document

# 先完成一个demo的文件版本，在完成项目版本

os.environ["OPENAI_API_BASE"] = 'http://localhost:11434/v1' 
os.environ["OPENAI_API_KEY"] ='NA'

# 获取大模型服务
llm = ChatOpenAI(model="ollama/qwen2.5:7b")

# 声明智能体
## 教学大纲的分析师
syllabusReader = Agent(
    role='教学大纲的分析师',
    goal='分析提供的章节范围，根据教学大纲，并从中提取出需要测试的知识点，并直接引用知识库中已学习的内容',
    backstory="""你是一位教学大纲的分析师，分析提供的章节范围，根据教学大纲，并从中提取出需要测试的知识点。
            请让你的回答尽可能详细包含要求中涵盖的教学大纲的各个知识点，
            你不需要说明实现的内容，尽量简洁，
            你的回答只包括多个对应chapter的多个对应的knowledgePoint中的name，如输出一个数组["为什么选择Python","Anaconda的安装与配置","Jupyter Notebook介绍"]
            下面是教学大纲：[    {        "chapter": "1",        "name": "Python语法基础",        "knowledgePoint": [            {"subId": "1.1", "name": "为什么选择Python?"},            {"subId": "1.2", "name": "Anaconda的安装与配置"},            {"subId": "1.3", "name": "Jupyter Notebook介绍"},            {"subId": "1.4", "name": "Python：基本输入输出"},            {"subId": "1.5", "name": "Python语法规范"},            {"subId": "1.6", "name": "Python简单数据类型"},            {"subId": "1.7", "name": "常量、变量和运算符"},            {"subId": "1.8", "name": "复合数据类型"}        ],        "command": "1)能够安装与配置Python环境；2)掌握Python代码规范；掌握Python的数据类型的使用、变量的应用、运算规则等",        "classTime": "3",        "experimentalTime": "0"    },    {        "chapter": "2",        "name": "Python程序结构，函数与模块",        "knowledgePoint": [            {"subId": "2.1", "name": "if/while/for语句"},            {"subId": "2.2", "name": "常用算法和应用案例"},            {"subId": "2.3", "name": "函数"},            {"subId": "2.4", "name": "变量作用域"},            {"subId": "2.5", "name": "闭包和递归函数调用"},            {"subId": "2.6", "name": "内置函数"},            {"subId": "2.7", "name": "模块"},            {"subId": "2.8", "name": "游戏示例"}        ],        "command": "1)能够使用Python三种基本程序结构（顺序结构、选择结构、循环结构）设计解决基本问题的逻辑代码；2)能够使用Python函数来有效编写代码，解决问题；3)能够解释函数参数的类型和传递，理解参数作用域；能够解释闭包和函数的递归调用的特性和作用等。",        "classTime": "6",        "experimentalTime": "0"    },    {        "chapter": "3",        "name": "面向对象程序设计和GUI",        "knowledgePoint": [            {"subId": "3.1", "name": "文件读写"},            {"subId": "3.2", "name": "数据存储"},            {"subId": "3.3", "name": "类的创建和使用"},            {"subId": "3.4", "name": "实例属性和类属性"},            {"subId": "3.5", "name": "私有成员和公有成员"},            {"subId": "3.6", "name": "面向对象编程应用"},            {"subId": "3.7", "name": "Tk图形用户界面"}        ],        "command": "1)理解面向对象程序设计的基本知识，包括类和对象，类的继承和多态；2)学会使用Tk接口设计带有用户界面的应用系统",        "classTime": "3",        "experimentalTime": "4"    },    {        "chapter": "4",        "name": "Python数据分析概述",        "knowledgePoint": [            {"subId": "4.1", "name": "Python数据分析学习路径"},            {"subId": "4.2", "name": "数据分析应用场景"}        ],        "command": "1)能够说明数据分析的应用场景；2)能够解释数据分析的常用工具；能够理解数据分析的学习路径",        "classTime": "2",        "experimentalTime": "0"    },    {        "chapter": "5",        "name": "Numpy数值计算基础",        "knowledgePoint": [            {"subId": "5.1", "name": "NumPy数组基础"},            {"subId": "5.2", "name": "数组的创建"},            {"subId": "5.3", "name": "NumPy数据类型"},            {"subId": "5.4", "name": "索引与切片"},            {"subId": "5.5", "name": "数组转置与轴交换"},            {"subId": "5.6", "name": "NumPy数组运算"},            {"subId": "5.7", "name": "面向数组编程"}        ],        "command": "1)掌握NumPy数组的基本操作；2)掌握NumPy数组的创建和数据类型；3)理解索引、切片和数组运算；4)掌握面向数组的编程方法",        "classTime": "3",        "experimentalTime": "0"    },    {        "chapter": "6",        "name": "Matplotlib数据可视化基础",        "knowledgePoint": [            {"subId": "6.1", "name": "基本绘图语法和常用参数"},            {"subId": "6.2", "name": "特征之间关系分析"},            {"subId": "6.3", "name": "特征内部分布和离散度分析"}        ],        "command": "1)掌握PyPlot基础语法与常用参数的作用；2)学会使用可视化工具分析特征间的关系；3)学会使用可视化工具了解特征内部数据分布与分散状况",        "classTime": "3",        "experimentalTime": "4"    },    {        "chapter": "7",        "name": "pandas统计分析",        "knowledgePoint": [            {"subId": "7.1", "name": "Pandas数据结构介绍"},            {"subId": "7.2", "name": "数据读写"},            {"subId": "7.3", "name": "DataFrame数据分析"},            {"subId": "7.4", "name": "时间序列数据处理"},            {"subId": "7.5", "name": "分组聚合计算"},            {"subId": "7.6", "name": "数据透视表和交叉表"},            {"subId": "7.7", "name": "Pandas数据预处理"}        ],        "command": "1)掌握pandas基础对象DataFrame的特征，学会如何创建、修改、查询该对象；2)学会读写不同数据源数据，包括Excel、文本、数据库数据；3)学会处理时间序列数据；4)使用分组聚合、创建透视表与交叉表进行数据分析；5)掌握合并数据，清洗数据，标准化数据，及转换数据等数据预处理方法。",        "classTime": "6",        "experimentalTime": "4"    },    {        "chapter": "8",        "name": "使用scikit-learn构建模型",        "knowledgePoint": [            {"subId": "8.1", "name": "使用sklearn预处理数据"},            {"subId": "8.2", "name": "构建和评聚类模型"},            {"subId": "8.3", "name": "构建和评估分类模型"},            {"subId": "8.4", "name": "构建和评估回归模型"}        ],        "command": "1)了解机器学习的概念；2)了解Python机器学习scikit-learn模块；3)学会使用scikit-learn进行模型构建、评估的方法。",        "classTime": "6",        "experimentalTime": "4"    }]""",
    allow_delegation=False,
    verbose=False,
    llm=llm
)

## 选择题出题大师
choiceQuestionMaker = Agent(
    role='选择题出题大师',
    goal='根据提供相关的Python课件，生成与上一级智能体生成的知识点相关的选择题，并提供批改和解析的方法，需要输出多道单选题的题目、答案和解析并直接引用知识库中已学习的内容',
    backstory=dedent("""
            你是一位专业的Python教学选择题出题专家。
            你的主要职责是根据提供的Python课件内容，生成高质量的选择题。
            你需要确保生成的选择题:
            1. 紧密贴合上一级智能体提供的知识点
            2. 题目表述清晰准确，没有歧义
            3. 选项设计合理，具有干扰性但不会产生误导
            4. 每道题都配有标准答案
            5. 解析详细充分，说明选择该答案的理由
            6. 难度适中，既能考察学生对知识点的掌握，又不会过于偏难怪
            7. 题型以单选题为主，每个知识点至少生成2-3道相关题目
            8. 答案解析要包含知识点的关键概念，帮助学生理解和记忆
            你要以专业、严谨的态度完成出题工作，确保题目的教学价值和考查效果。
            """),
    allow_delegation=False,
    verbose=False,
    llm=llm
)

## 编程题出题大师
programmingQuestionMaker = Agent(
    role='编程题出题大师',
    goal='根据提供相关的Python课件，生成与上一级智能体生成的知识点相关的编程题，并提供批改和解析的方法，需要输出多道编程题的题目、测试用例、答案和解析，并直接引用知识库中已学习的内容',
    backstory=dedent("""
            你是一位专业的Python教学编程题出题专家。
            你的主要职责是根据提供的Python课件内容，生成高质量的编程题。
            你需要确保生成的编程题:
            1. 紧密贴合上一级智能体提供的知识点
            2. 题目描述清晰准确，包含输入输出格式说明
            3. 提供多组测试用例，覆盖各种边界情况
            4. 每道题都配有参考答案和详细解析
            5. 解析要说明算法思路和关键代码实现
            6. 难度分级合理，从基础到提高
            7. 每个知识点至少生成2-3道相关题目
            8. 答案解析要包含知识点的关键概念和编程技巧
            你要以专业、严谨的态度完成出题工作，确保题目的教学价值和编程能力培养效果。
            """),
    allow_delegation=False,
    verbose=False,
    llm=llm
)

# 报告批改专家
reportCorrectionSpecialists = Agent(
    role='报告批改专家',
    goal='基于学生给的报告和设定的评分标准进行报告的批改',
    backstory=dedent("""
            你是老师教学助手，负责根据学生编程报告内容和评价要求，对报告进行客观评判，并提出建设性意见。你的能力有:
            1.分析报告内容，识别关键点
            2.根据知识库的评价标准，给出评分
            3.提出具体改进建议
            """),
    # 不允许委托
    allow_delegation=False,
    # tools=[file_tool],
    verbose=False,
    llm=llm
)

# 获取用户的输入，在这里定义各种变量
chapter = "第一、二、三章"
file_path = 'jsonData/大作业报告.docx'
student_report = """
"""
standard = """
### 一、小组作业
* **分工合理性 (10分**)
    * 每位组员的工作职责是否明确，是否涵盖数据获取、数据预处理、模型构建、模型评估、结果分析、报告撰写等各个环节。
    * 分工是否与每位组员的特长和能力相匹配。
    * 是否存在分工不均或职责不清的情况。
* **团队协作 (10分**)
    * 小组成员是否积极参与讨论和交流，共同解决问题。
    * 是否存在沟通不畅或合作不默契的情况。
    * 是否能够分享知识和经验，互相学习，共同进步。
* **独立担当 (10分**)
    * 每位组员是否能够独立完成分配的任务，并具备解决问题的能力。
    * 是否存在依赖他人或完成任务质量不高的情况。
### 二、内容
* **实践流程设计 (15分**)
    * 实践流程是否清晰、完整、合理，是否涵盖了数据获取、数据预处理、模型构建、模型评估、结果分析等各个环节。
    * 是否存在流程设计不合理或遗漏关键步骤的情况。
    * 是否使用了流程图或思维导图等工具清晰地展示实践流程。
* **程序开发 (10分**)
    * 程序代码是否完整，功能是否实现，运行是否稳定。
    * 代码注释是否清晰，命名是否规范，易于理解和维护。
    * 是否使用了代码版本控制工具，例如Git。
* **数据记录 (5分**)
    * 数据集的来源、结构、特征等信息是否清晰说明。
    * 数据预处理、特征工程等步骤是否详细记录，并解释其作用。
    * 是否使用了数据可视化工具，例如matplotlib或seaborn。
### 三、分析
* **结果分析 (10分**)
    * 是否对实验结果进行深入分析，解释其背后的原因和影响因素。
    * 是否能够根据实验结果提出有价值的见解和结论。
    * 是否能够发现实验过程中的问题和不足，并提出改进建议。
* **模型评估 (10分**)
    * 是否对模型性能进行评估，比较不同模型的优缺点。
    * 是否使用了多种评估指标，例如准确率、召回率、F1分数、AUC等。
    * 是否使用了交叉验证等方法来评估模型的泛化能力。
* **问题分析 (5分**)
    * 是否对实验过程中遇到的问题进行分析和解决。
    * 是否能够找到问题的根源，并提出有效的解决方案。
### 四、创新
* **流程设计创新 (10分**)
    * 是否在实践流程设计方面有所创新，例如使用了新的数据处理方法、新的模型或新的评估指标。
    * 创新点是否具有实用性和可行性，能够有效提高实验效果。
    * 是否对创新点进行了充分的论证和说明。
* **方法改进 (10分**)
    * 是否能够根据实践阶段性结果和最终结果进行分析，发现存在的问题和不足。
    * 是否能够针对问题提出改进方案，并采取具有创新性的实践方法进行改进。
    * 改进方案是否有效，是否能够显著提高实验效果。
    * 是否对改进过程进行了详细的记录和说明。
### 五、报告质量
* **内容完整性 (10分**)
    * 报告内容是否完整，是否涵盖了所有必要的部分，例如问题背景、目标、方法、结果、结论等。
    * 是否存在内容缺失或逻辑不清晰的情况。
* **内容准确性 (10分**)
    * 报告内容是否准确，是否真实反映了实验过程和结果。
    * 是否存在数据错误或结论错误的情况。
* **内容充实性 (5分**)
    * 报告内容是否充实，是否提供了足够的信息，例如数据集描述、代码解释、模型评估等。
    * 是否使用了图表、图像等可视化元素来展示结果。
* **格式规范性 (5分**)
    * 报告格式是否规范，是否符合要求，例如字体、字号、行距、页边距等。
* **文档完整性 (5分**)
    * 文档是否提交完整，包括代码文件、数据文件、报告文档等。
    注意要根据报告的具体内容看，对于每一条评判标准，只有在报告中找到对应的段落能印证才能认为达到标准，否则不算。
    评判时，如果找到能够印证的地方你要在评判中进行输出，告诉我报告的“（这里是写报告的内容）”地方这一条标准达到了。
"""

# 声明任务
## 章节 -> 知识点
ChapterToKnowledgePoints = Task(
    description=dedent(f"""
        请参考知识库中的内容，并根据提供的章节编号，分析教学大纲中对应章节的内容，提取出需要测试的知识点。
        你的输出应该是一个包含多个知识点名称的数组，每个知识点都应该来自教学大纲中对应章节的knowledgePoint列表。
        例如，如果输入章节一，你应该输出类似这样的数组：["为什么选择Python?", "Anaconda的安装与配置","Jupyter Notebook介绍","Python：基本输入输出","Python语法规范", "Python简单数据类型","常量、变量和运算符","复合数据类型"]
        请确保:
        1. 只输出知识点名称，不需要包含其他信息
        2. 所有知识点必须来自指定章节
        3. 输出格式必须是一个数组
        4. 保持知识点名称的原始表述，不要修改
        章节编号: {chapter}
    """),
    expected_output="知识点数组",
    agent=syllabusReader
)

# 知识点 -> 选择题目
ChapterToChoiceQuestions = Task(
    description=dedent("""
        请参考知识库中的内容，并根据上一级智能体提供的知识点列表，生成相应的选择题。
        每个知识点至少生成2-3道单选题，但是总体的题量不可以超过20题，每道题目需要按照以下格式输出:
        
        1. 题号.题干()
        A.选项A
        B.选项B 
        C.选项C
        D.选项D
        答案：A
        解析：详细解释答案的原因
        章节：第一章
                           
        例如:
        4、消防救援队伍作战训练行动，遵循（ ）的指导思想。
        A.救人第一、科学施救 
        B.安全第一、预防为主 
        C.生命至上  
        D.强攻近战、固移结合。
        答案：B
        解析：略
        章节：第一节
        
        要求:
        1. 题目描述清晰准确，没有歧义
        2. 4个选项设计合理，有干扰性但不误导
        3. 标准答案用括号标注在题干后
        4. 解析要说明选择该教学大纲的理由，包含知识点关键概念
        5. 输出题目时，不用说明知识点是哪个，只需要输出题目描述和要求说明
        
        题目难度要适中，既能考察学生对知识点的理解，又不会过于偏难。
        解析要帮助学生加深对知识点的理解和记忆。
        
    """),
    expected_output="包含题干、选项、答案和解析的单项选择题",
    context=[ChapterToKnowledgePoints],
    agent=choiceQuestionMaker,
    output_file='outputs/选择题.md',
)

# 知识点 ->编程题目
ChapterToProgrammingQuestion = Task(
    description=dedent("""
        请参考知识库中的内容，并根据上一级智能体提供的知识点列表，生成相应的编程题。
        每个知识点可以生成1道编程题，但总体题量不超过5题。
        每道题目需要按照以下格式输出:

        题号. 题目描述
        要求:
        1. 输入格式说明
        2. 输出格式说明
        3. 其他要求说明

        例如:
        1. 编写一个函数计算斐波那契数列的第n项
        要求:
        1. 输入一个正整数n
        2. 输出第n项斐波那契数
        3. n的取值范围为1-30

        题目要求:
        1. 题目描述要清晰准确
        2. 明确说明输入输出格式
        3. 难度要适中，循序渐进
        4. 紧密结合知识点
        
    """),
    expected_output="每个知识点1道编程题，总体不超过5题，包含题目描述和要求说明",
    context=[ChapterToKnowledgePoints],
    agent=programmingQuestionMaker,
    output_file='outputs/编程题.md',
)

# 批改报告
report_correction_task = Task(
    role='报告批改专家',
    goal='基于学生给的报告和设定的评分标准进行报告的批改',
    description=dedent(f"""
            根据学生上传的报告{student_report}和评分标准{standard}，进行报告批改。
            批改时，对评分标准的每一条标准进行评判，看报告是否达到标准，达不到应酌情扣分。
            """),
    expected_output="输出对每条标准的批改结果，包含该报告达到某一条标准的具体情况以及该标准的得分。并输出一个总分。",
    agent=reportCorrectionSpecialists,
    output_file='outputs/report_Docx批改.md',
)

choose = int(input().strip())
if choose == 1:
    # 定义任务顺序：选择题
    crew = Crew(
        agents=[
            syllabusReader,
            choiceQuestionMaker
        ], tasks=[
        ChapterToKnowledgePoints,
        ChapterToChoiceQuestions
        ],
        # 需要在输出中看到日志的信息
        verbose=True 
        # 不需要在输出中看到日志的信息
        # verbose=False
    )
else:
    if choose == 2:
        # 定义任务顺序 :编程题
        crew = Crew(
            agents=[
                syllabusReader,
                programmingQuestionMaker
            ], tasks=[
            ChapterToKnowledgePoints,
            ChapterToProgrammingQuestion
            ],
            # 需要在输出中看到日志的信息
            verbose=True
            # 不需要在输出中看到日志的信息
            # verbose=False
        )
    else:
        if choose == 3:
            #定义任务：批改报告
            crew = Crew(
                agents=[
                    reportCorrectionSpecialists
                ], tasks=[
                    report_correction_task
                ],
                # 需要在输出中看到日志的信息
                verbose=True
                # 不需要在输出中看到日志的信息
                # verbose=False
            )
        else:
            #待改动
            # 假设这是您提供的 .docx 文件路径
            file_path = "jsonData/大作业报告.docx"
            # 尝试读取 .docx 文件内容
            try:
                doc = Document(file_path)
                # 遍历文档中的所有段落
                for para in doc.paragraphs:
                    student_report += para.text + "\n"  # 将每个段落的文本添加到 content 变量中

                # 遍历文档中的所有表格
                for table in doc.tables:
                    for row in table.rows:
                        for cell in row.cells:
                            student_report += cell.text + " "  # 将每个单元格的文本添加到 content 变量中
                        student_report += "\n"  # 每读取完一行，添加一个换行符
                    student_report += "\n\n"  # 每读取完一个表格，添加两个换行符

            except Exception as e:
                print(f"发生错误：{e}")
            print(student_report)
            crew = Crew(
                agents=[
                    reportCorrectionSpecialists
                ], tasks=[
                    report_correction_task
                ],
                # 需要在输出中看到日志的信息
                verbose=True
                # 不需要在输出中看到日志的信息
                # verbose=False
            )
# inputs={"chapter": "第一、二、三章"}
# result = crew.kickoff(inputs=inputs)

result = crew.kickoff()
print("下面是打印的输出结果：",result)
